//@version: $Id: Map.cpp 153 2010-12-11 00:41:06Z bills.jared $

#include "Map.h"

void Map::clear() {
	//clear the entire combo list
	m_vMarkedCombos.clear();
}

const bool Map::comboCheck() {
	bool bComboFound = false;
	Shape * pvLastShape;
	unsigned int uiComboCounter;

	//go through the map grid of shapes horizontally
	for(unsigned int uiY = 0; uiY < HEIGHT; ++uiY) {
		//reset the combo counter and last shape
		pvLastShape = &m_vGrid[uiY][0];
		uiComboCounter = 0;
		for(unsigned int uiX = 0; uiX < WIDTH; ++uiX) {
			//if last grid shape is the same type as this
			if(m_vGrid[uiY][uiX].getShapeTyp() == pvLastShape->getShapeTyp()) {
				//increase combo counter and if it's equal to COMBO_MAX
				if((++uiComboCounter) == COMBO_MAX) {
					//then record combo as found
					bComboFound = true;
					//create a new combo list
					Combo vTemp;
					//go through the last COMBO_MAX number of shapes
					for(unsigned int uiI = 0; uiI < COMBO_MAX; ++uiI) {
						unsigned int uiTempCoord = uiX - uiI;
						//store the coordinates of the shapes
						vTemp.comboCoords[uiI].x = uiTempCoord;
						vTemp.comboCoords[uiI].y = uiY;
						//then store the shapes in the list
						vTemp.comboShapes[uiI] = &m_vGrid[uiY][uiTempCoord];
					}
					//add it to the combo list
					m_vMarkedCombos.add(vTemp);
					//and reset the combo counter
					uiComboCounter = 0;
				}
			}
			//else if grid shape is different type from last
			else
				//reset the combo counter
				uiComboCounter = 1;
			//set the last shape to this shape
			pvLastShape = &m_vGrid[uiY][uiX];
		}
		//reset the combo counter
		uiComboCounter = 1;
	}
	
	//go through the map grid of shapes vertically
	for(unsigned int uiX = 0; uiX < WIDTH; ++uiX) {
		//reset the combo counter and last shape
		pvLastShape = &m_vGrid[0][uiX];
		uiComboCounter = 0;
		for(unsigned int uiY = 0; uiY < HEIGHT; ++uiY) {
			//if last grid shape is the same type as this
			if(m_vGrid[uiY][uiX].getShapeTyp() == pvLastShape->getShapeTyp()) {
				//increase combo counter and if it's equal to COMBO_MAX
				if((++uiComboCounter) == COMBO_MAX) {
					//then record combo as found
					bComboFound = true;
					//create a new combo list
					Combo vTemp;
					//go through the last COMBO_MAX number of shapes
					for(unsigned int uiI = 0; uiI < COMBO_MAX; ++uiI) {
						unsigned int uiTempCoord = uiY - uiI;
						//store the coordinates of the shapes
						vTemp.comboCoords[uiI].x = uiX;
						vTemp.comboCoords[uiI].y = uiTempCoord;
						//then store the shapes in the list
						vTemp.comboShapes[uiI] = &m_vGrid[uiTempCoord][uiX];
					}
					//add it to the combo list
					m_vMarkedCombos.add(vTemp);
					//and reset the combo counter
					uiComboCounter = 0;
				}
			}
			//else if grid shape is different type from last
			else
				//reset the combo counter
				uiComboCounter = 1;
			//set the last shape to this shape
			pvLastShape = &m_vGrid[uiY][uiX];
		}
		//reset the combo counter
		uiComboCounter = 1;
	}
	//return true if combo is found
	return bComboFound;
}

const bool Map::comboCheckQuick() {
	bool bComboFound = false;
	Shape * pvLastShape;
	unsigned int uiComboCounter;

	//go through the map grid of shapes horizontally
	for(unsigned int uiY = 0; uiY < HEIGHT && !bComboFound; ++uiY) {
		//reset the combo counter and last shape
		pvLastShape = &m_vGrid[uiY][0];
		uiComboCounter = 0;
		for(unsigned int uiX = 0; uiX < WIDTH && !bComboFound; ++uiX) {
			//if last grid shape is the same type as this
			if(m_vGrid[uiY][uiX].getShapeTyp() == pvLastShape->getShapeTyp()) {
				//increase combo counter and if it's equal to COMBO_MAX
				if((++uiComboCounter) == COMBO_MAX)
					//then record combo as found
					bComboFound = true;
			}
			//else if grid shape is different type from last
			else
				//reset the combo counter
				uiComboCounter = 1;
			//set the last shape to this shape
			pvLastShape = &m_vGrid[uiY][uiX];
		}
		//reset the combo counter
		uiComboCounter = 1;
	}
	
	//go through the map grid of shapes vertically
	for(unsigned int uiX = 0; uiX < WIDTH && !bComboFound; ++uiX) {
		//reset the combo counter and last shape
		pvLastShape = &m_vGrid[0][uiX];
		uiComboCounter = 0;
		for(unsigned int uiY = 0; uiY < HEIGHT && !bComboFound; ++uiY) {
			//if last grid shape is the same type as this
			if(m_vGrid[uiY][uiX].getShapeTyp() == pvLastShape->getShapeTyp()) {
				//increase combo counter and if it's equal to COMBO_MAX
				if((++uiComboCounter) == COMBO_MAX)
					//then record combo as found
					bComboFound = true;
			}
			//else if grid shape is different type from last
			else
				//reset the combo counter
				uiComboCounter = 1;
			//set the last shape to this shape
			pvLastShape = &m_vGrid[uiY][uiX];
		}
		//reset the combo counter
		uiComboCounter = 1;
	}
	//return true if combo is found
	return bComboFound;
}

const bool Map::swapQuick(const unsigned int & a_uiAx, const unsigned int & a_uiAy, const unsigned int & a_uiBx, const unsigned int & a_uiBy) {
	//if any coordinates are out of bounds
	if(a_uiAx >= WIDTH || a_uiBx >= WIDTH || a_uiAy >= HEIGHT || a_uiBy >= HEIGHT)
		//return as failure to swap
		return false;
	//store shape type A temporarily in a temp variable
	int iTempType = m_vGrid[a_uiAy][a_uiAx].getShapeTyp();

	//swap the shape types between the shapes
	m_vGrid[a_uiAy][a_uiAx].setShapeType(m_vGrid[a_uiBy][a_uiBx].getShapeTyp());
	m_vGrid[a_uiBy][a_uiBx].setShapeType(iTempType);

	//return as successful swap
	return true;
}

void Map::handleClickInput() {
	Location vDifference;
	Location vFirstClick, vSecondClick;
	bool bSwapPossible;

	//record the first two clicks
	vFirstClick = m_vClickBuffer[0].mouseClick();
	vSecondClick = m_vClickBuffer[1].mouseClick();
	//record the difference between the first two clicks
	vDifference.x = vSecondClick.x - vFirstClick.x;
	vDifference.y = vSecondClick.y - vFirstClick.y;
	//set swap possible only if clicks are seperate from each other, and next to each other horizontally or vertically
	bSwapPossible = ((!vDifference.x || vDifference.x == 1 || vDifference.x == -1) && (!vDifference.y || vDifference.y == 1 || vDifference.y == -1));
	bSwapPossible &= ((vDifference.x != NULL) ^ (vDifference.y != NULL));
	//if swap possible
	if(bSwapPossible) {
		//then swap based on the two clicks
		if(swap(vFirstClick.x, vFirstClick.y, vSecondClick.x, vSecondClick.y)) {
			if(!comboCheckQuick())
				swap(vFirstClick.x, vFirstClick.y, vSecondClick.x, vSecondClick.y);
		}
	}
}



Map::Map() : m_uiCurrentClick(0) {}
Map::~Map() {}



const bool Map::init() {
	bool bInitSuccess = true;
	rotateState = false;
	g_pStopWatch = new StopWatch;
	assert(g_pStopWatch != NULL);
	//go through the entire 2D array
    for(unsigned int uiY = 0; uiY < HEIGHT; ++uiY) {
		for(unsigned int uiX = 0; uiX < WIDTH; ++uiX) {
            //initialize each individual shape
			/*bInitSuccess &= */m_vGrid[uiY][uiX].init();
			m_vGrid[uiY][uiX].translateGridLoc(uiX, uiY);
		}
	}
	//gets rid of any and all sets before giving it to the user
	while(this->comboRenew()); //sets were found, cleared, and map was filled

	return bInitSuccess;
}

const bool Map::swap(const unsigned int & a_uiAx, const unsigned int & a_uiAy, const unsigned int & a_uiBx, const unsigned int & a_uiBy) {
	//if any coordinates are out of bounds
	if(a_uiAx >= WIDTH || a_uiBx >= WIDTH || a_uiAy >= HEIGHT || a_uiBy >= HEIGHT)
		//return as failure to swap
		return false;
	//store shape type A temporarily in a temp variable
	int iTempType = m_vGrid[a_uiAy][a_uiAx].getShapeTyp();

	//swap the shape types
	m_vGrid[a_uiAy][a_uiAx].setShapeType(m_vGrid[a_uiBy][a_uiBx].getShapeTyp());
	m_vGrid[a_uiBy][a_uiBx].setShapeType(iTempType);

	//return as successful swap
	return true;
}

const bool Map::check() {
	bool bCombosPossible = false;

	//go through each shape in the map
	for(unsigned int uiY = 0; uiY < HEIGHT && !bCombosPossible; ++uiY) {
		for(unsigned int uiX = 0; uiX < WIDTH && !bCombosPossible; ++uiX) {
			//go through horizontal and vertical phases of swapping per current shape
			for(unsigned int uiSwapDirection = HORIZONTAL; uiSwapDirection < COMBO_SIZE && !bCombosPossible; ++uiSwapDirection) {

				//record values for horizontal and vertical phases
				unsigned int uiHorizontal = uiX + ((COMBO_SIZE-1)-uiSwapDirection);
				unsigned int uiVertical = uiY + uiSwapDirection;

				//swap the current shape horizontally or vertically (dependent on the phase)
				//   Note: Does not swap if values are out of bounds (internal in swapQuick function)
				if(swapQuick(uiX, uiY, uiHorizontal, uiVertical)) {
					//if successful, do a check for new combos
					if(comboCheckQuick())
						//set combos as possible if new combo found
						bCombosPossible = true;
					//swap the current shape back to its position
					swapQuick(uiX, uiY, uiHorizontal, uiVertical);
				}
			}
		}
	}

	//return true if combos are possible
	return bCombosPossible;
}

void Map::fill() {
	for(int j = 0; j < m_vMarkedCombos.size(); j++)
	{
        //Starts the current combination. Because there may be more than one combo.
        MapCoord * currentPoint = m_vMarkedCombos.get(j).comboCoords;
        //Puts the current set of coordinates into a pointer for easier access.
        for(int i = COMBO_MAX-1; i >= 0; i--)
        {
            //Sets it to go backwards through the coordinates, forwards creates a bug (has to do with way coordinates are set.)
            for(int k = currentPoint[i].y; k > 0; k--)
            {
				m_vGrid[k][currentPoint[i].x].setShapeType(m_vGrid[k-1][currentPoint[i].x].getShapeTyp());
                //"Hey, you're above me, GO RIGHT HERE." "Ok." - two random shapes, one of which is in a combination.
            }
			Shape * temp = &m_vGrid[0][currentPoint[i].x];
			temp->seed(SHAPE_SIZE);
			temp->setShapeType(temp->generate());
            //Randomizes the shape that's at Y coordinate 0.
        }
    }
}

const bool Map::comboRenew() {
	bool bCombosReplaced;

	//if there are shape combos available
	if(bCombosReplaced = comboCheck()) {
		//fill in the voids with new shapes
		fill();
		//clear the combo shapes
		clear();
	}

	return bCombosReplaced;
}

void Map::handleInput(UserInput a_vUI)
{
	//take in next mouse click
	//   Note: Expect this code to change to be able to accept different UI types, other than mouse
	//      Ex: a_vUI.getType() == KEYBOARD
	m_vClickBuffer[m_uiCurrentClick++] = a_vUI;
	m_uiCurrentClick%=CLICK_MAX;

	//if max clicks were reached
	//   Note: Expect this code to change to use flag system with switch
	if(!m_uiCurrentClick)
		//then handle the clicks
		handleClickInput();
}

void Map::drawAll(){
	//create temp list
	ObjectList templist;
	//create list for object updates
	ObjectList updatelist;
	//Get time since previous call to updates
	const double cdDeltaTime = g_pStopWatch->GetDeltaT();

	//go through the entire 2D array
    for(unsigned int uiY = 0; uiY < HEIGHT; ++uiY) {
        for(unsigned int uiX = 0; uiX < WIDTH; ++uiX) {
            //draw each individual shape
			//the row and column are being passed so the grid coordinates can be changed to world coordinates
			//m_vGrid[uiY][uiX].draw();
			InsertObject(m_vGrid[uiY][uiX].draw(), templist);

        }
    }

	/* BUG: if you swap the teardrop in the first column with the sphere below
	** it, the following loop crashes because of a pointer being NULL.
	*/

	//call all the objects update routine in templist and they will update themselves
	//according to how much time has gone by and get reinserted into a new list
	//that gets drawn later
	for each(Object* pObj in templist)
	{
		if(pObj->Update(cdDeltaTime))
		{
			InsertObject(pObj,updatelist);
		}
	}
	Renderer::Instance()->RenderOneFrame(&updatelist);
	templist.clear();
	updatelist.clear();
}

//Updated to make the toggle function work correctly.
void Map::spinAll()
{
	//Ugly little toggle
	if(rotateState == true)
	{
		rotateState = false;
	}
	else
	{
		rotateState = true;
	}

	for(int i = 0; i < HEIGHT; ++i)
	{
		for(int k = 0; k < WIDTH; ++k)
		{
			m_vGrid[i][k].setSpin(rotateState);
			//Goes through the array and sets all Shapes to spin.
		}
	}
}

Shape Map::getShapeAt(UserInput a_vUI){
	Location mouseLocation = a_vUI.getLocationOfMouse();
	return m_vGrid[mouseLocation.y][mouseLocation.x];
}

Shape& Map::getShapeAt(Location& a_loc){
	assert((a_loc.x < WIDTH) && (a_loc.x >= 0) && (a_loc.y < HEIGHT) && (a_loc.y >= 0));
	return m_vGrid[a_loc.y][a_loc.x];
}
//this will reset the map by changing the shapeNumbers
void Map::resetMap(){
	//go through and set a random shape number for each shape
	for(int r = 0; r < HEIGHT; r++)
	{
		for(int c = 0; c < WIDTH; c++)
		{
			m_vGrid[r][c].setShapeType(m_vGrid[r][c].generate());
		}
	}
	//then checks to see if there is a match then clears it
	while(this->comboRenew());
}